﻿#region Using

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;

using CommunicationLibrary.TransportData;

using GameLogic.External;

using ServerApplication.Enums;
using ServerApplication.Interfaces;
using ServerApplication.Networking;

#endregion

namespace ServerApplication.GameSession
{
    public class PreSession
    {
        private readonly IGame game;

        private readonly IReadySessionManager manager;

        private readonly Dictionary<ServerPlayer, bool> readyList = new Dictionary<ServerPlayer, bool>();

        private readonly Timer timeOutTimer;

        private bool gameStarted;

        public PreSession(IEnumerable<ServerPlayer> players, IReadySessionManager manager)
        {
            var serverPlayers = players as IList<ServerPlayer> ?? players.ToList();
            foreach (var serverPlayer in serverPlayers)
            {
                this.readyList.Add(serverPlayer, serverPlayer.ServerPlayerState == ServerPlayerState.Disconnected);
            }

            var gameStart = GameFactory.GenerateGame(serverPlayers);
            this.game = gameStart.Item1;
            this.StartingState = new GameState(gameStart.Item2);

            this.timeOutTimer = new Timer(10000);
            this.timeOutTimer.AutoReset = false;
            this.timeOutTimer.Elapsed += this.HandleTimeOut;
            this.timeOutTimer.Start();

            this.gameStarted = false;

            this.manager = manager;
        }

        public GameState StartingState { get; private set; }

        public bool Contains(ServerPlayer player)
        {
            return this.readyList.ContainsKey(player);
        }

        public void MarkReady(ServerPlayer player)
        {
            lock (this.readyList)
            {
                this.readyList[player] = true;
                new Task(this.CheckAllReady).Start();
            }
        }

        private void HandleTimeOut(object sender, ElapsedEventArgs e)
        {
            lock (this.readyList)
            {
                foreach (var pair in this.readyList.Where(x => x.Value == false).ToList())
                {
                    this.timeOutTimer.Stop();
                    this.readyList[pair.Key] = true;
                    pair.Key.ServerPlayerState = ServerPlayerState.Disconnected;
                }

                this.CheckAllReady();
            }
        }

        private void CheckAllReady()
        {
            lock (this.readyList)
            {
                if (this.readyList.Values.All(x => x) && !this.gameStarted)
                {
                    this.gameStarted = true;
                    this.manager.StartGame(this.game, this.readyList.Keys, this);
                }
            }
        }
    }
}